#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <fstream>

#include "tests.h"
#include "logint.h"
#include "query.h"
#include "projects.h"
#include "program.h"
#include "antlradapter.h"
#include "util.h"

using namespace std;
/*
Tests::Tests(bool Is_GUI)
{
///Instantiate the variables of the object
IsGUI=Is_GUI;
CurrentState=ChooseProject;
C_LR=new AntlrAdapter();
Print+="Please choose the project you want to work on:\n";
DeleteProgram=false;
///Make sure the user has some projects that he can work on. Otherwise no need for him to proceed

///Temporary file to include objdump stuff needed to generate the report, automatically deleted later on 
int FileOpens=system("touch elieshaccour");

if(FileOpens==-1)
	{
	;//TODO: Report Error
	}
}
*/
Tests::Tests(bool Is_GUI, std::vector<std::string> Test_Criteria, std::vector<std::string> Test_Files,string ProjectPath)
{
IsGUI=Is_GUI;
TestCriteria=Test_Criteria;
TestFile=Test_Files;
CurrentState=GenerateTest;
C_LR=new AntlrAdapter();
ProjectDirectoryPath=ProjectPath;
Print+="Type 'all' to generate test requirements for all the test criteria:\n";
DeleteProgram=true;
CurrentProgram=new Program(ProjectPath);
CurrentProgram->Process();
///Temporary file to include objdump stuff needed to generate the report, automatically deleted later on 
int FileOpens=system("touch elieshaccour");

if(FileOpens==-1)
        {
        ;//TODO: Report Error
        }
cout<<"Size of the tests="<<TestCriteria.size()<<endl;

for(unsigned int temp=0;temp<TestCriteria.size();temp++)
	cout<<"test criteria:"<<TestCriteria.at(temp)<<" exercised by file:"<<TestFile.at(temp)<<endl;
}

void Tests::GoForward(string UserInput)
{
///Depending on state can chose what to do
DeleteProgram=true;
//// Create temp files that would help us get the loc information 
///Compile with -g flags
string SystemCompileCommand="g++ -g -o temp ";
SystemCompileCommand+=ProjectDirectoryPath;
int SystemReturn=system(SystemCompileCommand.c_str());
///objdump to get loc and address
string SystemObjdumpCommand="objdump -d -l temp > elieshaccour";
SystemReturn=system(SystemObjdumpCommand.c_str());
SystemReturn=system("rm temp");
SetLineNumbersToAssemblyAddressesMapping();

if(UserInput.compare("all")==0)///Will provide capabilities to exclude some tests already set, but for now only option is choosing 'all' tests previously set
		{
		Print="";
		/// TODO: multithread this process and fill back into a vector the answers and then change vector into criteria 
		/// Loop over all TC and get their new position in the file, printed in a way according to function getrequirements(x,y)
		for(unsigned int temp=0;temp<TestCriteria.size();temp++)
			Print+=GetRequirements(TestCriteria.at(temp),TestFile.at(temp));
		}
	else /*!TODO: provide option to exclude some tests */
		{
		Print="";
                for(unsigned int temp=0;temp<TestCriteria.size();temp++)
                        Print+=GetRequirements(TestCriteria.at(temp),TestFile.at(temp));
		}
if(IsGUI==false)
        {
        Print+="Press 'logout' to go to main menu\n";
        }
}

int Tests::IsValid()
{
if(CurrentState==Invalid)
	return 0;
else return 1;

}


string Tests::GetPrint()
{
return Print;
}

void Tests::GoBackward()
{
;///TODO: go backward option
}

string Tests::GetRequirements(string Test_Criteria, string Test_File)
{
string NotImplemented="Required Testing Criteria Not Implemented";

if(Test_Criteria[0]=='d' && Test_Criteria[1]=='u')
	return GetDefUseRequirements(Test_Criteria, Test_File);
else
	return NotImplemented;
}

string Tests::GetDefUseRequirements(string Test_Criteria, string Test_File)
{
/*! First Step is to break up the function automatically with program.h */
/*! This will be done in the main function as a change of project should be reflected directly from above */

/*! Second Step is to parse the line of the requirements into def, use */
string Def="";
string Use="";
unsigned int temp=3;

for( ; temp<Test_Criteria.length() && Test_Criteria[temp]!=')' ; temp++)
        Def+=Test_Criteria[temp];

for( ; temp<Test_Criteria.length() && Test_Criteria[temp]!=',' ; temp++)
	Def+=Test_Criteria[temp];
temp++;

while(Test_Criteria[temp]==' ')
	temp++;

for( ; temp<Test_Criteria.length() && Test_Criteria[temp]!='>' ; temp++)
	Use+=Test_Criteria[temp];
	

//GET Function_name, line scope and descritpion for the def
string DefFunctionName="";
int DefLineScope;
string DefLineScopeString="";
string DefDescription="";
temp=0;

while((temp<Def.length())&(Def[temp]!='.'))
	{
	DefFunctionName+=Def[temp++];
	}

while((temp<Def.length())&(Def[temp]!='='))
	{
	temp++;
	}

temp++;
while((temp<Def.length())&(Def[temp]!='.'))
        {
        DefLineScopeString+=Def[temp++];
        }

DefLineScope=atoi(DefLineScopeString.c_str());

temp++;
while((temp<Def.length())&(Def[temp]!=' '))
        {
        DefDescription+=Def[temp++];
        }
//End of getting function_name, line scope and description for the def

//GET Function_name, line scope and descritpion for the use
string UseFunctionName="";
int UseLineScope;
string UseLineScopeString="";
string UseDescription="";
temp=0;

while((temp<Use.length())&(Use[temp]!='.'))
        {
        UseFunctionName+=Use[temp++];
        }

while((temp<Use.length())&(Use[temp]!='='))
        {
        temp++;
        }

temp++;
while((temp<Use.length())&(Use[temp]!='.'))
        {
        UseLineScopeString+=Use[temp++];
        }

UseLineScope=atoi(UseLineScopeString.c_str());

temp++;
while((temp<Use.length())&(Use[temp]!=' '))
        {
        UseDescription+=Use[temp++];
        }
//End of getting function_name, line scope and description for the use

int DefNewLinePosition=0,UseNewLinePosition=0;
/*! Sub-Step 1: Find the function index in the object CurrentProgram*/
int FunctionPosition=0;
for(int temp=0;temp<CurrentProgram->GetFunctionsCount();temp++)
	{
	if(UseFunctionName.compare(CurrentProgram->GetFunctionNameAtPosition(temp))==0)
		{
			FunctionPosition=temp;
		} 
	}

/*! Sub-Step 2: Find the line scope of the def and use*/
DefNewLinePosition=CurrentProgram->FindLineNewPosition(FunctionPosition,DefLineScope,DefDescription,"def");
UseNewLinePosition=CurrentProgram->FindLineNewPosition(FunctionPosition,UseLineScope,UseDescription,"use");

/*! Third Step is to generate the requirements */
string RequirementsString=GetAssemblyAddressForLineAtPosition(DefNewLinePosition);
RequirementsString+=" before ";
RequirementsString+=GetAssemblyAddressForLineAtPosition(UseNewLinePosition);
RequirementsString+=" for TestFile=";
RequirementsString+=Test_File;
RequirementsString+="\n";

return RequirementsString;
}

Tests::~Tests()
{
//clean the files that are temporarily created for getting the new positions
string DeleteTempFile="rm elieshaccour";
int temp=system(DeleteTempFile.c_str());
if (temp==-1)
	{
	;//TODO:Report Error
	}
delete C_LR;
if(DeleteProgram==true)
	delete CurrentProgram;
}


void Tests::SetLineNumbersToAssemblyAddressesMapping()///Get the mapping of all the lines in the program with the assembly address they are at
{
///open the objdump file to get the assembly address of the du-pairs
string Line="";
ifstream ObjdumpFile("elieshaccour");
if(ObjdumpFile.is_open())
	{
	while(!ObjdumpFile.eof())
		{
		getline(ObjdumpFile,Line);
		if(Line[0]=='/')///Output of objdump starts with '/' for every line in source code
			{
			string NumberString="";
			unsigned int temp=0;
			while((Line[temp++]!=':')&&(temp<Line.length()))///line number is specified after the only ':' at the end of the line
				;
			
			while(temp<Line.length())///Get line number
				{
				NumberString+=Line[temp++];
				}

			int LineNumberTemp=atoi(NumberString.c_str());

			getline(ObjdumpFile,Line,':');///After line number comes the block that will execute it. I am only interested in the address of the first line in the block
			/*! remove WS before the asm_address */
			string AssemblyAddressString="";
			for(unsigned int temp=1;temp<Line.length();temp++)
				AssemblyAddressString+=Line[temp];

			///push the line address and the assembly address on the specific lines
			LineNumbers.push_back(LineNumberTemp);
			AssemblyAddresses.push_back(AssemblyAddressString);
			}
		}
	}
}


string Tests::GetAssemblyAddressForLineAtPosition(int LinePosition)
{//Since the lines are set get the assembly address for line_number

string AssemblyAddress="0x";
for(unsigned int temp=0;temp<LineNumbers.size();temp++)///Search the vector for the exact line number
	{
	if(LineNumbers.at(temp)==LinePosition)
		{
		AssemblyAddress+=AssemblyAddresses.at(temp);
		return AssemblyAddress;
		}
	}

return "invalid line number";//TODO: report error
}
